package com.unittec.drainage.gateway.filter;

import com.unittec.drainage.gateway.common.GateWayConstants;
import com.unittec.drainage.gateway.common.GatewayUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpStatus;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.netflix.zuul.filters.Route;
import org.springframework.cloud.netflix.zuul.filters.RouteLocator;
import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
import org.springframework.web.util.UrlPathHelper;

import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import com.unittec.drainage.provider.common.ServiceFuncUtils;
import com.unittec.drainage.provider.log.Log;
import com.unittec.drainage.provider.scope.ThreadLocalScope;
import com.unittec.drainage.provider.service.HystrixRestTemplateService;
import com.unittec.drainage.sdk.auth.response.Res10000;
import com.unittec.drainage.sdk.root.domain.GatewayRequest;
import com.unittec.drainage.sdk.root.domain.Result;
import com.unittec.drainage.sdk.root.enums.BizCode;
import com.unittec.drainage.sdk.root.internal.utils.SdkUtils;

/**
 * 应用的请求参数通过鉴权检查后，在这里根据路由信息动态路由到对应的业务服务
 */
public class GatewayRouteLocatorFilter extends ZuulFilter {

    @Autowired
    Log log;

    private final RouteLocator routeLocator;
    private final UrlPathHelper urlPathHelper = new UrlPathHelper();
    private final HystrixRestTemplateService hystrixRestTemplateService;

    public GatewayRouteLocatorFilter(RouteLocator routeLocator, String dispatcherServletPath,
        HystrixRestTemplateService hystrixRestTemplateService) {
        this.routeLocator = routeLocator;
        this.hystrixRestTemplateService = hystrixRestTemplateService;
    }

    @Override
    public String filterType() {
        return FilterConstants.PRE_TYPE;
    }

    @Override
    public int filterOrder() {
        return FilterConstants.PRE_DECORATION_FILTER_ORDER + 1;
    }

    @Override
    public boolean shouldFilter() {
        RequestContext ctx = RequestContext.getCurrentContext();
        return (Boolean)ctx.get(GateWayConstants.CANRUN);
    }

    private void failResponse(RequestContext ctx, String msg) {
        // 过滤该请求，不对其进行路由
        ctx.setSendZuulResponse(false);
        ctx.setResponseStatusCode(HttpStatus.SC_OK);
        GatewayUtils.writer(ctx.getResponse(), SdkUtils
            .toJSON(Result.getewayError((GatewayRequest)ctx.get(GateWayConstants.SERVICECONTENT), BizCode.ERROR, msg)));
    }

    // 接口限流
    // private Result<Res10007> limit(RequestContext ctx) {
    // GatewayRequest gatewayData = (GatewayRequest) ctx.get(GateWayConstants.SERVICECONTENT);
    // log.sysInfoLog("limit", "requestId=[{}] appId=[{}] method=[{}] tenantId=[{}]",
    // gatewayData.getRequestId(), gatewayData.getAppId(), gatewayData.getMethod(),
    // ThreadLocalScope.getAuthData().getTenantId());
    //
    // GatewayRequest<Req10007> request = new GatewayRequest<Req10007>();
    // request.setMethod("10007");
    // request.setAppId(gatewayData.getAppId());
    // request.setAppSettingId(gatewayData.getAppSettingId());
    //
    // Req10007 content = new Req10007();
    // content.setFunctionCode(gatewayData.getMethod());
    // request.setContent(content);
    // Result<Res10007> result = null;
    //
    // try {
    // String token = ctx.getZuulRequestHeaders().get(FieldConstants.REQ_AUTHORIZATION.toLowerCase());
    // request.setToken(token);
    // ThreadLocalScope.AuthData authData = ThreadLocalScope.getAuthData();
    // result =
    // hystrixRestTemplateService.exchange(Md5.getMd5(FieldConstants.SYS_REQUEST_SOURCE_ID),GateWayConstants.APPLIMITURL,
    // request, authData, new ParameterizedTypeReference<Result<Res10007>>() {
    // });
    // if (!result.isSuccess()) {
    // return result;
    // }
    // } catch (Exception e) {
    // return Result.error(gatewayData, e.toString());
    // }
    //
    // return null;
    // }

    @Override
    public Object run() throws ZuulException {
        RequestContext ctx = RequestContext.getCurrentContext();
        String requestUri = this.urlPathHelper.getPathWithinApplication(ctx.getRequest());
        Route route = this.routeLocator.getMatchingRoute(requestUri);
        if (null == route) {
            // 获取鉴权功能通过后，需要路由的服务接口名称
            String serviceName = (String)ctx.get(GateWayConstants.SERVICEMETHED);
            // 根据服务接口名称获取对应的实际服务信息
            try {
                Res10000 service = ServiceFuncUtils.fetchService(serviceName);
                if (null != service && StringUtils.isNotBlank(service.getServiceUrl())) {
                    requestUri = service.getServiceId() + service.getServiceUrl();
                    route = this.routeLocator.getMatchingRoute("/" + requestUri);
                    if (null != route) {
                        // Result<Res10007> limitRet = limit(ctx);
                        // 设置context
                        ctx.set(FilterConstants.SERVICE_ID_KEY, service.getServiceId());
                        ctx.put(FilterConstants.REQUEST_URI_KEY, service.getServiceUrl());
                        ctx.put(FilterConstants.PROXY_KEY, service.getServiceId());
                        ctx.put(FilterConstants.RETRYABLE_KEY, true);
                        // // 系统内部请求,下级微服务不在验证服务信息是否存在
                        // ctx.addZuulRequestHeader(FieldConstants.SYS_REQUEST_SOURCE,
                        // Md5.getMd5(FieldConstants.SYS_REQUEST_SOURCE_ID));
                        ctx.remove(FilterConstants.FORWARD_TO_KEY);
                        String logmsg = String.format("router url=[%s] requestId=[%s] appId=[%s] tenantId=[%s]",
                            requestUri, ThreadLocalScope.getAuthData().getRequestId(),
                            ThreadLocalScope.getAuthData().getAppId(), ThreadLocalScope.getAuthData().getTenantId());
                        log.sysInfoLog("router", logmsg);

                    } else {
                        ServiceFuncUtils.deleteService(serviceName);
                        this.failResponse(ctx, "没有路由信息, 请检查服务运行是否正常");
                    }
                } else {
                    this.failResponse(ctx, "接口没有注册, 请检查接口是否在注册中心注册");
                }
            } catch (Exception e) {
                this.failResponse(ctx, e.getMessage());
            }
        }
        return null;
    }
}
